2022/12/27現在 サポート終了によりUnityAsset Store から削除されているためアセットを取得することができなくなっています
Chronosを利用することで
- ゲーム全体を遅く/速くする (グローバル
- 特定のグループを持つゲームオブジェクトのみを遅く/早くする (ローカル
- 特定のColliderに触れている間のみ特定の速度に変化させる (エリア
といったゲームオブジェクトそれぞれをカスタイマイズした時間管理ができるようになります。 (公式サイトの動画を見るのが一番わかり易いです) https://ludiq.io/chronos/features
最初にChronosのコンポーネント、実装がどのような形になっているかを見ていきます。
Timekeeper
まず、Chronosで時間を管理するには TimeKeeper
と呼ばれるゲーム全体の管理者となるオブジェクトが必要です。
これはシングルトンオブジェクトでシーンに一つだけ存在します。
Timekeeperは Clock
クラスを管理しています
Clockクラス
適用するTimeScaleの値を持ち、毎フレーム更新処理が呼び出され適用する時間を計算するクラス
つまり Clock クラスが実際の時間管理を行い、このクラスが管理しているGameObjectに対して変数に持っているTimeScaleの値を適用させています
Clockクラスには以下3種類存在
GlobalClock | 複数のClock,Timeline を内部に保持する。string型のKeyを持つ。 |
---|---|
LocalLock | アタッチされているGameObjectのみを管理する。 |
AreaClock | 球体のエリア以内に入ったTImelineを持つGameObjectのタイムスケールを変更する |
各種Clockの説明は以下の図がわかりやすいです。
TimekeeperがTopに存在し、GlobalClockの親子構造、一番下にGameObjectにアタッチされているTimelineが存在しています
例えば、上部にある Root のKeyを持つ Global Clock
の一時停止処理を呼び出すと、その下についている全てのTimelineが一時停止(Timescale: 0)されます。
- 敵だけを遅く/早くしたい場合は Enemies のKeyを持つGlobal Clock を取得してTImescale値を変更させる。
- Playerだけ(一番右下)早くしたい場合は、PlayerについているLocalClock(Player)のTimescaleを変更する。
ということが出来ます。
各種構造化させておき、Timescaleをジャンルごとに自由に操れるのがChronosパッケージの強みです
Timeline
Chronosの時間操作を適用させたいGameObjectには必ずTimelineを付ける必要があります。
以下設定値
Mode | string | Local: LocalClockを検索して適用。Global: GlobalClockを検索して自分に適用 |
---|---|---|
Global Clock | string | ModeがGlobalのときのみ有効。自分に適用するGlobalClockのKeyを指定 |
Rewindable | bool | 巻き戻しのサポート。公式の動画を見るのがわかりやすいです。。 |
RecordingDuration | float | ※Rewindable true時のみ。記録最大継続時間(秒)。値を大きくするとその分巻き戻し時間も増えるがより多くのメモリも必要とする |
RecordingInterval | float | ※Rewindable true時のみ。スナップショットの記録間隔(秒)。値を低くするとその分メモリを消費します |
時間を制御したいGameObjectにTimelineを付け、Mode, Global Clock を適切に設定するだけで動くようになります。
Timelineの中身を見るとわかりますが、Awakeメソッドで
- Animator
- Animation
- AudioSource
- NavMeshAgent
- ParticleSystem
- WindZone
- Terrain
- TrailRenderer
- 各種Rigidbody
のコンポーネントが自分についているかGetComponentで調べ、それに合ったタイムラインを内部で生成しています
// 例えばAnimator
var animatorComponent = GetComponent<Animator>();
if (animator == null && animatorComponent != null)
{
animator = new AnimatorTimeline(timeline, animatorComponent);
animator.Initialize();
components.Add(animator);
}
else if (animator != null && animatorComponent == null)
{
animator = null;
}
// Animatorのspeedを変更するカスタムクラス
public class AnimatorTimeline : ComponentTimeline<Animator>
{
...
}
ComponentTimelineのジェネリッククラスを生成しているためつまり自分でもカスタムクラスを作成することが出来ます (これはまた別記事で)
GlobalClockの実装
試しにGlobalClockの実装を行ってみます。
2つのGameObjetを作成しました。それぞれ
- Capsule : Animator
- Cube : RigidBody.velocity
で動かしています。
この2つのGameObjectをChronosで制御してみます
兎にも角にもTImekeeperがいるため作成。
Create → TimeKeeper
を選択
そしてゲームオブジェクトにChronosの各種コンポーネントをつけることでそのオブジェクトの時間を管理します
作成されたTimekeeperGameObjectには2つのコンポーネントがついています
- Timekeeper
- Global Clock
そこに2つのGlobalClockをアタッチさせて名前を
- GlobalA → Capsuleの時間操作
- GlobalB → Cubeの時間操作
としてみます。
ParentにはRootを選択して親子構造をつくります。
各GameObjectにTimelineを付けます Animatorの方はGroupA RigidBodyの方はGroupBoxとします
GroupA の GlobalClock の TImescale を 0.1 にして再生してみます
GroupAのCapsuleがゆっっくりと移動するようになりました。
速度が上がりました。
GroupClockではKey名でGorup化し、特定GroupのTimescaleを制御する手法を取っています。
KeyはStringですがEditor機能でサポートしているため打ち間違いによるヒューマンエラーが起きないような設計になっています
Group化しているため、同じGroupを持つオブジェクトはスピードが同期します
Local Clock
キューブの一つを Local
にしてみます。
LocalClockをアタッチ
この状態で再生
手前のキューブはTimescaleが1のLocalなので特に動きが変わらなくなりました。
LocalClock は世界に一つしか無いGameObject、例えば Player や演出用の環境オブジェクトにつけるのが良さそうです。
スクリプトから変更
スクリプトから特定のGroupのTimescale値を変更してみます Timekeeper はシングルトンなため、TimekeeperからGroup名で検索しTimescaleを変更します
void Start()
{
// GropuAを2バイの速さにする
var clock = Timekeeper.instance.Clock("GroupA");
clock.localTimeScale = 2f;
}
Group名で検索し(ここはstring) localTimescale を設定 これでGroupAのCapsuleの速度が2倍になりました
RIgidBodyのvelocityを操作する際には注意が必要になります
RIgidbodyを制御する RigidbodyTimeline3D.cs
には以下のプロパティが存在します
/// <summary>
/// The velocity of the rigidbody before time effects. Use this property instead of Rigidbody.velocity, which will be overwritten by the physics timer at runtime.
/// </summary>
public Vector3 velocity
{
get { return bodyVelocity / timeline.timeScale; }
set { if (AssertForwardProperty("velocity", Severity.Ignore)) bodyVelocity = value * timeline.timeScale; }
}
つまり、コード内で rigidbody.velocity を書く際には、Timescaleか考慮された RigidbodyTimeline3Dが持つvelocityを利用する必要があります。
こちらは移行マニュアルを見て各種適切に処理する必要があります
最後に
Unityでは時間制御のためにTimescaleを変更することが多いですが、全てのGameObejctが影響受けてしまうため一部のGameObjectは遅くしたくない/速くしたくない。などのケースでは Chronos を利用してプロジェクトを構築していくのが良さそうです。 コード自体もシンプルに作られているため中身を見てカスタマイズするのも容易です。
しかしTimelineを全てのGameObjectにつける必要があるため、途中から考慮するのは難しくなります また、他のサードパーティ製品ではChronosをサポートしてないケースが殆んどのため注意です